package com.zui.framework.utils
{
	
	import flash.utils.Dictionary;

	public class HashMap
	{
		
		private var length:int;
		private var content:Dictionary;
		
		public function HashMap(){
			length = 0;
			content = new Dictionary();
		}
		
		//-------------------public methods--------------------
		
		/**
		 * Returns the number of keys in this HashMap.
		 */
		public function size():int{
			return length;
		}
		
		/**
		 * Returns if this HashMap maps no keys to values.
		 */
		public function isEmpty():Boolean{
			return (length==0);
		}
		
		/**
		 * Returns an Array of the keys in this HashMap.
		 */
		public function keys():Array{
			var temp:Array = new Array(length);
			var index:int = 0;
			for(var i:* in content){
				temp[index] = i;
				index ++;
			}
			return temp;
		}
		
		/**
		 * Call func(key) for each key.
		 * @param func the function to call
		 */
		public function eachKey(func:Function):void{
			for(var i:* in content){
				func(i);
			}
		}
		
		/**
		 * Call func(value) for each value.
		 * @param func the function to call
		 */ 	
		public function eachValue(func:Function):void{
			for each(var i:* in content){
				func(i);
			}
		}
		
		/**
		 * Returns an Array of the values in this HashMap.
		 */
		public function values():Array{
			var temp:Array = new Array(length);
			var index:int = 0;
			for each(var i:* in content){
				temp[index] = i;
				index ++;
			}
			return temp;
		}

		
		/**
		 * Tests if some key maps into the specified value in this HashMap. 
		 * This operation is more expensive than the containsKey method.
		 */
		public function containsValue(value:*):Boolean{
			for each(var i:* in content){
				if(i === value){
					return true;
				}
			}
			return false;
		}
		
		/**
		 * Tests if the specified object is a key in this HashMap.
		 * This operation is very fast if it is a string.
		 * @param   key   The key whose presence in this map is to be tested
		 * @return <tt>true</tt> if this map contains a mapping for the specified
		 */
		public function containsKey(key:*):Boolean{
			if(content[key] != undefined){
				return true;
			}
			return false;
		}
		
		/**
		 * Returns the value to which the specified key is mapped in this HashMap.
		 * Return null if the key is not mapped to any value in this HashMap.
		 * This operation is very fast if the key is a string.
		 * @param   key the key whose associated value is to be returned.
		 * @return  the value to which this map maps the specified key, or
		 *          <tt>null</tt> if the map contains no mapping for this key
		 *           or it is null value originally.
		 */
		public function get(key:*):*{
			var value:* = content[key];
			if(value !== undefined){
				return value;
			}
			return null;
		}
		
		/**
		 * Same functionity method with different name to <code>get</code>.
		 * 
		 * @param   key the key whose associated value is to be returned.
		 * @return  the value to which this map maps the specified key, or
		 *          <tt>null</tt> if the map contains no mapping for this key
		 *           or it is null value originally.
		 */
		public function getValue(key:*):*{
			return get(key);
		}
		
		/**
		 * Associates the specified value with the specified key in this map. 
		 * If the map previously contained a mapping for this key, the old value is replaced. 
		 * If value is null, means remove the key from the map.
		 * @param key key with which the specified value is to be associated.
		 * @param value value to be associated with the specified key. null to remove the key.
		 * @return previous value associated with specified key, or <tt>null</tt>
		 *	       if there was no mapping for key.  A <tt>null</tt> return can
		 *	       also indicate that the HashMap previously associated
		 *	       <tt>null</tt> with the specified key.
		 */
		public function put(key:*, value:*):*{
			if(key == null){
				throw new ArgumentError("cannot put a value with undefined or null key!");
				return undefined;
			}else if(value == null){
				return remove(key);
			}else{
				var exist:Boolean = containsKey(key);
				if(!exist){
					length++;
				}
				var oldValue:* = this.get(key);
				content[key]=value;
				return oldValue;
			}
		}
		
		/**
		 * Removes the mapping for this key from this map if present.
		 *
		 * @param  key key whose mapping is to be removed from the map.
		 * @return previous value associated with specified key, or <tt>null</tt>
		 *	       if there was no mapping for key.  A <tt>null</tt> return can
		 *	       also indicate that the map previously associated <tt>null</tt>
		 *	       with the specified key.
		 */
		public function remove(key:*):*{
			var exist:Boolean = containsKey(key);
			if(!exist){
				return null;
			}
			var temp:* = content[key];
			delete content[key];
			length--;
			return temp;
		}
		
		/**
		 * Clears this HashMap so that it contains no keys no values.
		 */
		public function clear():void{
			length = 0;
			content = new Dictionary();
		}
		
		/**
		 * Return a same copy of HashMap object
		 */
		public function clone():HashMap{
			var temp:HashMap = new HashMap();
			for(var i:* in content){
				temp.put(i, content[i]);
			}
			return temp;
		}
		
		public function toString():String{
			var ks:Array = keys();
			var vs:Array = values();
			var temp:String = "HashMap Content:\n";
			for(var i:int=0; i<ks.length; i++){
				temp += ks[i]+" -> "+vs[i] + "\n";
			}
			return temp;
		}		
	}
	
}